//
// Created by jaesonzhang on 2024/9/12.
//

#include <android/log.h>
#include "JSAppBinding.h"
#include "engine/JSApp.h"

#define TAG "JNI_TAG"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)


#define GET_NATIVE_POINTER(env, obj, fieldID, type) \
    ((fieldID == nullptr) ? nullptr : reinterpret_cast<type*>(env->GetLongField(obj, fieldID)))

#define SIMPLE_CODE(env, obj, fieldID, type, method) \
    JSApp *app = GET_NATIVE_POINTER(env, thiz, g_pointerFieldID, JSApp); \
    if(app) app->method();


// 全局变量
static std::once_flag g_initFlag;
jfieldID g_pointerFieldID = nullptr;
jmethodID g_getJSAppCodeMethodID = nullptr;


extern "C"
JNIEXPORT void JNICALL
start(JNIEnv *env, jobject thiz) {
  SIMPLE_CODE(env, thiz, g_pointerFieldID, JSApp, start)
}

extern "C"
JNIEXPORT void JNICALL
stop(JNIEnv *env, jobject thiz) {
  SIMPLE_CODE(env, thiz, g_pointerFieldID, JSApp, stop)
}

extern "C"
JNIEXPORT void JNICALL
destroy(JNIEnv *env, jobject thiz) {
  SIMPLE_CODE(env, thiz, g_pointerFieldID, JSApp, destroy)
}

extern "C"
JNIEXPORT void JNICALL
halt(JNIEnv *env, jobject thiz) {
  SIMPLE_CODE(env, thiz, g_pointerFieldID, JSApp, halt)
}
extern "C"
JNIEXPORT void JNICALL
resume(JNIEnv *env, jobject thiz) {
  SIMPLE_CODE(env, thiz, g_pointerFieldID, JSApp, resume)
}


extern "C"
JNIEXPORT void JNICALL
attachSurface(JNIEnv *env,
              jobject thiz,
              jobject jSurface,
              jint width,
              jint height) {
  JSApp *app = GET_NATIVE_POINTER(env, thiz, g_pointerFieldID, JSApp);
  if (app) {
    app->attachSurface(jSurface, width, height);
  }
}

extern "C"
JNIEXPORT void JNICALL
detachSurface(JNIEnv *env,
              jobject thiz,
              jobject jSurface) {
  JSApp *app = GET_NATIVE_POINTER(env, thiz, g_pointerFieldID, JSApp);
  if (app) {
    app->detachSurface(jSurface);
  }
}

extern "C"
JNIEXPORT jlong JNICALL
createNativeJsApp(JNIEnv *env, jobject thiz) {
  auto app = new JSApp(env->NewGlobalRef(thiz));
  return reinterpret_cast<jlong>(app);
}

extern "C"
JNIEXPORT void JNICALL
destroyNativeJsApp(JNIEnv *env, jobject thiz, jlong nativeAppPtr) {
  // 将 jlong 转换回智能指针并删除对象
  JSApp *app = reinterpret_cast<JSApp *>(nativeAppPtr);
  delete app;
}

static const char *jsAPPClassName = "com/compose/skia/JSApp";
static const JNINativeMethod jsAPPMethods[] = {
    {"createNativeJsApp", "()J", (void *) createNativeJsApp},
    {"destroyNativeJsApp", "(J)V", (void *) destroyNativeJsApp},
    {"start", "()V", (void *) start},
    {"stop", "()V", (void *) stop},
    {"destroy", "()V", (void *) destroy},
    {"halt", "()V", (void *) halt},
    {"resume", "()V", (void *) resume},
    {"attachSurface", "(Landroid/view/Surface;II)V", (void *) attachSurface},
    {"detachSurface", "(Landroid/view/Surface;)V", (void *) detachSurface},
};

jint bindingJSApp(JNIEnv *env) {
  // 获取类引用
  jclass clazz = env->FindClass(jsAPPClassName);
  if (!clazz) {
    LOGE("can not find class");
    return -1;
  }
  if (env->RegisterNatives(clazz, jsAPPMethods,
                           sizeof(jsAPPMethods) / sizeof(jsAPPMethods[0]))
      < 0) {
    return -1;
  }

  // 设置全局变量
  std::call_once(g_initFlag, [&]() {
    g_pointerFieldID = env->GetFieldID(clazz, "pointer", "J");
    g_getJSAppCodeMethodID =
        env->GetMethodID(clazz, "getJSAppCode", "()Ljava/lang/String;");
  });
  return 0;
}
